<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>作用域-let-const块级作用域应用</title>
  <!-- 
  在JavaScript中， `let`  和  `const`  的块级作用域特性为变量的管理和代码结构提供了更大的灵活性和安全性。
  1. 块级作用域的定义
  块级作用域是指变量的作用范围仅限于其被声明的代码块（即  `{}`  内部）。在块外部无法访问块内部声明的变量。这种特性使得变量的作用范围更加明确，减少了命名冲突和意外修改的风险。

  2. 应用场景
   2.1 循环中的块级作用域
  在循环中，使用  `let`  声明的变量每次迭代都会创建一个新的作用域。这对于处理异步操作尤其重要。
  for (let i = 0; i < 3; i++) {
      setTimeout(() => {
          console.log(i); // 输出: 0, 1, 2
      }, 1000);
  }
  在这个例子中，每次迭代的  `i`  都是一个新的变量，因此  `setTimeout`  中的  `i`  能够正确输出每次迭代的值。

   2.2 函数中的块级作用域
  在函数中使用块级作用域可以提高代码的可读性，避免全局变量污染。
  function example() {
      let a = 10;
      if (true) {
          let b = 20;
          console.log(a); // 输出: 10
          console.log(b); // 输出: 20
      }
      console.log(b); // 报错: b is not defined
  }

  example();
  在这个例子中，变量  `a`  在整个函数中可用，而  `b`  仅在  `if`  块中可用，避免了在函数外部访问  `b`  的风险。

  3. 变量重名问题
  块级作用域允许在不同的块中使用相同的变量名，而不会造成冲突。这对于复杂的代码结构非常有用。
  let x = 1;

  if (true) {
      let x = 2;
      console.log(x); // 输出: 2
  }

  console.log(x); // 输出: 1
  在这个例子中，外部的  `x`  和内部块的  `x`  是不同的变量，互不影响。

  4. 使用  `const`  声明常量
  使用  `const`  声明的常量也具有块级作用域，这确保了常量的值在其作用域内不会被意外修改。
  {
      const PI = 3.14;
      console.log(PI); // 输出: 3.14
  }

  // console.log(PI); // 报错: PI is not defined
  在这个例子中，常量  `PI`  仅在其块内有效，外部无法访问。

  5. 总结
  - 增强代码可读性：块级作用域使得变量的作用范围更加清晰，减少了命名冲突和意外修改的风险。
  - 适用于循环和条件语句：在循环和条件语句中使用  `let`  和  `const`  可以有效管理变量的作用域，特别是在处理异步操作时。
  - 避免全局污染：通过使用块级作用域，可以防止全局变量的污染，提高代码的模块化程度。
  - 提高维护性：块级作用域使得代码更易于理解和维护，特别是在大型项目中。

  理解和应用  `let`  和  `const`  的块级作用域特性，有助于编写更安全、可维护的 JavaScript 代码。
  -->
</head>

<body>
  <button>按钮1</button>
  <button>按钮2</button>
  <button>按钮3</button>

  <script>
    // 1. 循环中的块级作用域
    // 在 ES6 之前，循环中的变量声明通常使用  `var`  关键字，这会导致变量提升和全局污染的问题。而使用  `let`  声明的变量具有块级作用域，避免了这些问题。
    for (var i = 0; i < 3; i++) {
      setTimeout(function () {
        console.log("循环中的块级作用域 var", i); // 输出: 3 3 3
      }, 1000);
    }

    for (var i = 0; i <= 3; i++) {
      setTimeout(function () {
        console.log("循环中的块级作用域 var", i); // 输出: 4 4 4 4
      }, 1000);
    }

    for (let i = 0; i < 3; i++) {
      setTimeout(function () {
        console.log("循环中的块级作用域 let", i); // 输出: 0 1 2
      }, 1000);
    }

    // for (const i = 0; i < 3; i++) { // Uncaught TypeError: Assignment to constant variable.
    //   setTimeout(function () {
    //     console.log("循环中的块级作用域 const", i); // 输出: 0 1 2
    //   }, 1000);
    // }

    // 2. 条件语句中的块级作用域
    if (true) {
      let a = 10;
      const b = 20;
      var c = 30;
    }
    // console.log(a); // 报错: a is not defined
    // console.log(b); // 报错: b is not defined
    console.log(c); // 输出: 30

    // 3. 函数中的块级作用域
    function foo() {
      if (true) {
        let x = 10;
      }
      // console.log(x); // 报错: x is not defined
    }

    // 4. 嵌套函数中的块级作用域
    function bar() {
      if (true) {
        function baz() {
          console.log("嵌套函数中的块级作用域");
        }
        baz();
      }
    }
    bar(); // 输出: 嵌套函数中的块级作用域

    // 5. 监听按钮点击事件中的块级作用域
    const btnEls = document.querySelectorAll("button");
    for (let i = 0; i < btnEls.length; i++) {
      btnEls[i].addEventListener("click", function () {
        console.log("按钮点击事件中的块级作用域", i); // 输出: 0 1 2
      });
    }
  </script>
</body>

</html>